﻿using System;
using System.Collections;
using System.Data;
using System.Data.SqlClient;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Data.Mapping;

namespace FileServer.DataConnectors
{
	/// <summary>
	/// Retrieves data from the database and serves content to the DBContentProvider
	/// </summary>
	public class DataServer
	{
        //private string _connectionString = null;
        //private string ConnectionString
        //{
        //    get
        //    {
        //        return _connectionString;
        //    }
        //}


		private FileServerEntities Connection;
        //private FileServerEntities Connection
        //{
        //    get
        //    {
        //        if (_connection == null)
        //        {
        //            _connection = new FileServerEntities();
        //        }
        //        return _connection;
        //    }
        //}


		private DataTable _data;
		private DataTable Data
		{
			get
			{
				if (_data == null)
				{
                    using (Connection = new FileServerEntities())
                    {
                        var query = from o in Connection.Items
                                    orderby o.ItemID
                                    select new
                                    {
                                        ItemID = o.ItemID,
                                        Name = o.Name,
                                        ParentID = o.ParentID,
                                        MimeType = o.MimeType,
                                        IsDirectory = o.IsDirectory,
                                        Size = o.Size
                                    };
                        _data = LINQToDataTable(query);
                    }
					
				}
				return _data;
			}
		}


        public DataTable LINQToDataTable<T>(IEnumerable<T> varlist)
        {
            DataTable dtReturn = new DataTable();

            // column names 
            PropertyInfo[] oProps = null;

            if (varlist == null) return dtReturn;

            foreach (T rec in varlist)
            {
                // Use reflection to get property names, to create table, Only first time, others will follow 
                if (oProps == null)
                {
                    oProps = ((Type)rec.GetType()).GetProperties();
                    foreach (PropertyInfo pi in oProps)
                    {
                        Type colType = pi.PropertyType;

                        if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>)))
                        {
                            colType = colType.GetGenericArguments()[0];
                        }

                        dtReturn.Columns.Add(new DataColumn(pi.Name, colType));
                    }
                }

                DataRow dr = dtReturn.NewRow();

                foreach (PropertyInfo pi in oProps)
                {
                    dr[pi.Name] = pi.GetValue(rec, null) == null ? DBNull.Value : pi.GetValue
                    (rec, null);
                }

                dtReturn.Rows.Add(dr);
            }
            return dtReturn;
        }

		/// <summary>
		/// Creates an instance of the DataServer class
		/// </summary>
		/// <param name="connectionString"></param>
        //public DataServer(string connectionString)
        //{
        //    _connectionString = connectionString;
        //}


        //private void CloseConnection()
        //{
        //    if (Connection.State != ConnectionState.Closed)
        //    {
        //        Connection.Close();
        //    }
        //}

		private int GetItemId(string path)
		{
			DataRow itemRow = GetItemRow(path);
			if (itemRow == null)
			{
				return -1;
			}
			return (int)itemRow["ItemID"];
		}

		public DataRow[] GetChildDirectoryRows(string path)
		{
			return Data.Select(string.Format("ParentID={0} AND IsDirectory", GetItemId(path)));
		}

		public DataRow[] GetChildFileRows(string path)
		{
			return Data.Select(string.Format("ParentID={0} AND NOT IsDirectory", GetItemId(path)));
		}

		public DataRow GetItemRow(string path)
		{
			if (path.EndsWith("/"))
			{
				path = path.Substring(0, path.Length - 1);
			}
			string[] names = path.Split('/');
			//Start search in root;
			DataRow searchedRow = null;
			int itemId = 0;
			for (int i = 0; i < names.Length; i++)
			{
				string name = names[i];
				DataRow[] rows = Data.Select(string.Format("Name='{0}' AND (ParentID={1} OR {1}=0)", name.Replace("'", "''"), itemId));
				if (rows.Length == 0)
				{
					return null;
				}
				searchedRow = rows[0];
				itemId = (int)searchedRow["ItemID"];
			}
			return searchedRow;
		}
		public byte[] GetContent(string path)
		{
			int itemId = GetItemId(path);
			if (itemId <= 0)
			{
				return null;
			}
            Connection = new FileServerEntities();
			byte[] returnValue = Connection.Items.Where(i => i.ItemID == itemId).FirstOrDefault().Content;
			return returnValue;
		}

		public void CreateItem(string name, string path, string mimeType, bool isDirectory, long size, byte[] content)
		{
			int parentId = GetItemId(path);
			if (parentId < 0)
			{
				return;
			}
            Connection = new FileServerEntities();
            
                Item newItem = new Item();
                newItem.Name = name;
                newItem.ParentID = parentId;
                newItem.MimeType = mimeType;
                newItem.IsDirectory = isDirectory;
                newItem.Size = int.Parse(size.ToString());
                newItem.Content = content;
                Connection.Items.AddObject(newItem);
                Connection.SaveChanges();
            
			_data = null;
		}

		public void DeleteItem(string path)
		{
			//TODO: cascade delete when removing a non-empty folder
			int itemId = GetItemId(path);
			if (itemId <= 0)
			{
				return;
			}
            Connection = new FileServerEntities();
            
                var item = Connection.Items.Where(i => i.ItemID == itemId).FirstOrDefault();
                Connection.Attach(item);
                Connection.Items.DeleteObject(item);
                Connection.SaveChanges();
            
			_data = null;
		}

		public DataRow[] GetAllDirectoryRows(string path)
		{
			DataRow rootRow = GetItemRow(path);
			if (rootRow == null)
			{
				return new DataRow[] { };
			}
			ArrayList allDirectoryRows = new ArrayList();
			allDirectoryRows.Add(rootRow);
			FillChildDirectoryRows((int)rootRow["ItemID"], allDirectoryRows);
			return (DataRow[])allDirectoryRows.ToArray(typeof(DataRow));
		}

		private void FillChildDirectoryRows(int parentId, ArrayList toFill)
		{
			DataRow[] childRows = Data.Select(string.Format("ParentID={0} AND IsDirectory", parentId));
			foreach (DataRow childRow in childRows)
			{
				toFill.Add(childRow);
				FillChildDirectoryRows((int)childRow["ItemID"], toFill);
			}
		}

		public string GetItemPath(DataRow row)
		{
			if (row["ParentID"] is DBNull)
			{
				return string.Format("{0}/", row["Name"]);
			}
			int parentId = (int)row["ParentID"];
			DataRow[] parents = Data.Select(string.Format("ItemID={0}", parentId));
			if (parents.Length == 0)
			{
				return string.Format("/{0}", row["Name"]);
			}
			return GetItemPath(parents[0]) + string.Format("{0}/", row["Name"]);
		}

		public void ReplaceItemContent(string path, byte[] content)
		{
			int itemId = GetItemId(path);
			if (itemId < 0)
			{
				return;
			}
            Connection = new FileServerEntities();
            
                var item = Connection.Items.Where(i => i.ItemID == itemId).FirstOrDefault();
                item.Content = content;
                Connection.Attach(item);
                Connection.ObjectStateManager.ChangeObjectState(item, EntityState.Modified);
                Connection.SaveChanges();
            
		}
		public void UpdateItemPath(string path, string newName, int newParentId)
		{
			int itemId = GetItemId(path);
			if (itemId < 0)
			{
				return;
			}
            Connection = new FileServerEntities();
            
                var item = Connection.Items.Where(i => i.ItemID == itemId).FirstOrDefault();
                item.Name = newName;
                item.ParentID = newParentId;
                Connection.Attach(item);
                Connection.ObjectStateManager.ChangeObjectState(item, EntityState.Modified);
                Connection.SaveChanges();
            
		}

		/// <summary>
		/// Checks whether the item(file or folder) exists on the server
		/// </summary>
		/// <param name="pathToItem">Path to file or folder</param>
		/// <returns></returns>
		public bool IsItemExists(string pathToItem)
		{
			DataRow row = GetItemRow(pathToItem);

			return row != null;
		}
	}
}